import os
import pathlib
import re
import sys
import logging
from functools import lru_cache
from fastapi import FastAPI
from pydantic_settings import BaseSettings
from pydantic_core import ValidationError
from app.utils.regexes import PASSWORD

# 日志器
uvicorn_logger = logging.getLogger('uvicorn')

# 静态文件资源目录
STATIC_FOLDER = pathlib.Path('static')
STATIC_FOLDER.mkdir(parents=True, exist_ok=True)
STATIC_IMG_FOLDER = STATIC_FOLDER / 'images'
STATIC_IMG_FOLDER.mkdir(parents=True, exist_ok=True)

# 禁止随意修改
# 需要自定义请参考本地 .env 配置
ENVIRONMENTS = ('dev', 'prod')
ENVIRON_NAME = os.getenv('PING_CARD_ENV', ENVIRONMENTS[0])
if ENVIRON_NAME not in ENVIRONMENTS:
    uvicorn_logger.error(f'Invalid environment：{ENVIRON_NAME}')
    sys.exit(0)

ENVIRON_FILE = f'config.{ENVIRON_NAME}.env'
ENVIRON_FILE_ENCODING = 'utf-8'
if not pathlib.Path(ENVIRON_FILE).exists():
    uvicorn_logger.error(f'Environment file({ENVIRON_FILE}) not found!')
    sys.exit(0)

MIN_SESSION_EXPIRE_MINUTES = 5


class Settings(BaseSettings):
    VERSION: str
    POSTGRESQL_URL: str
    REDIS_URL: str
    AUTH_SESSION_REDIS_DB: int
    CARD_TOKEN_REDIS_DB: int
    API_NONCE_REDIS_DB: int
    BACKEND_NONCE_REDIS_DB: int
    BACKEND_DFP_REDIS_DB: int
    BACKEND_IP_REDIS_DB: int
    DFP_EXPIRE_DAYS: int
    IP_EXPIRE_DAYS: int
    SUPER_USERNAME: str
    SUPER_PASSWORD: str
    SUPER_EMAIL: str
    SESSION_EXPIRE_MINUTES: int
    CARD_TOKEN_SECRET_KEY: str
    CLEAN_CARD_LOGIN_TIMES: str
    ENABLE_FAKER_API: bool

    class Config:
        env_file = ENVIRON_FILE
        env_file_encoding = ENVIRON_FILE_ENCODING

    @property
    def is_dev(self):
        return ENVIRON_NAME == ENVIRONMENTS[0]

    @property
    def is_prod(self):
        return ENVIRON_NAME == ENVIRONMENTS[1]


@lru_cache
def get_settings():
    try:
        return Settings()
    except ValidationError as e:
        uvicorn_logger.error(f'Environment file({ENVIRON_FILE}) validation error! Below details:')
        uvicorn_logger.error(e)
        sys.exit(0)


def mount_settings_to_app(app: FastAPI):
    app.settings = get_settings()


uvicorn_logger.info('Begin checking environment file...')

# 数据库配置检测
if not get_settings().POSTGRESQL_URL:
    uvicorn_logger.error('POSTGRESQL_URL not found!')
    sys.exit(0)

if not get_settings().REDIS_URL:
    uvicorn_logger.error('REDIS_URL not found!')
    sys.exit(0)

if type(get_settings().AUTH_SESSION_REDIS_DB) is not int or not (0 <= get_settings().AUTH_SESSION_REDIS_DB <= 15):
    uvicorn_logger.error('AUTH_SESSION_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().CARD_TOKEN_REDIS_DB) is not int or not (0 <= get_settings().CARD_TOKEN_REDIS_DB <= 15):
    uvicorn_logger.error('CARD_TOKEN_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().API_NONCE_REDIS_DB) is not int or not (0 <= get_settings().API_NONCE_REDIS_DB <= 15):
    uvicorn_logger.error('API_NONCE_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().BACKEND_NONCE_REDIS_DB) is not int or not (0 <= get_settings().BACKEND_NONCE_REDIS_DB <= 15):
    uvicorn_logger.error('BACKEND_NONCE_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().BACKEND_DFP_REDIS_DB) is not int or not (0 <= get_settings().BACKEND_DFP_REDIS_DB <= 15):
    uvicorn_logger.error('BACKEND_DFP_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().BACKEND_IP_REDIS_DB) is not int or not (0 <= get_settings().BACKEND_IP_REDIS_DB <= 15):
    uvicorn_logger.error('BACKEND_IP_REDIS_DB must be an integer between 0 and 15!')
    sys.exit(0)

if type(get_settings().DFP_EXPIRE_DAYS) is not int or get_settings().DFP_EXPIRE_DAYS <= 0:
    uvicorn_logger.error('DFP_EXPIRE_DAYS must be an integer greater than 0!')
    sys.exit(0)

if type(get_settings().IP_EXPIRE_DAYS) is not int or get_settings().IP_EXPIRE_DAYS <= 0:
    uvicorn_logger.error('IP_EXPIRE_DAYS must be an integer greater than 0!')
    sys.exit(0)

if get_settings().CLEAN_CARD_LOGIN_TIMES:
    times = get_settings().CLEAN_CARD_LOGIN_TIMES.split(':', 1)
    if len(times) != 2 or not times[0].isdigit() or not times[1].isdigit():
        uvicorn_logger.error('CLEAN_CARD_LOGIN_TIMES must be in the format of "0:0"!')
        sys.exit(0)

# 超级管理员配置检测
if not get_settings().SUPER_EMAIL:
    uvicorn_logger.error('SUPER_EMAIL not found!')
    sys.exit(0)

if not (6 <= len(get_settings().SUPER_USERNAME) <= 32):
    uvicorn_logger.error('SUPER_USERNAME must be between 6 and 32 characters!')
    sys.exit(0)

if not re.search(re.compile(PASSWORD), get_settings().SUPER_PASSWORD):
    uvicorn_logger.error('SUPER_PASSWORD must comply with:')
    uvicorn_logger.error('1. At least 1 number')
    uvicorn_logger.error('2. At least 1 uppercase letter and 1 lowercase letter')
    uvicorn_logger.error('3. At least 1 special character (@+#_)')
    sys.exit(0)

# 认证配置检测
if get_settings().SESSION_EXPIRE_MINUTES < MIN_SESSION_EXPIRE_MINUTES:
    uvicorn_logger.error(f"ACCESS_TOKEN_EXPIRE_MINUTES must be greater than {MIN_SESSION_EXPIRE_MINUTES}")
    sys.exit(0)

if len(get_settings().CARD_TOKEN_SECRET_KEY) != 64:
    uvicorn_logger.error('CARD_TOKEN_SECRET_KEY must be 64 characters!')
    sys.exit(0)

uvicorn_logger.info('Environment file check complete!')
